Update spec.
authorAnders Carlsson <andersca@imendio.com>
Tue, 15 Mar 2005 13:18:25 +0000 (13:18 +0000)
committerAnders Carlsson <andersca@src.gnome.org>
Tue, 15 Mar 2005 13:18:25 +0000 (13:18 +0000)
2005-03-15  Anders Carlsson  <andersca@imendio.com>

* docs/iconcache.txt:
Update spec.

* gtk/gtkiconcache.c: (find_image_offset),
(_gtk_icon_cache_get_icon_flags), (_gtk_icon_cache_add_icons),
(_gtk_icon_cache_get_icon), (_gtk_icon_cache_get_icon_data):
* gtk/gtkiconcache.h:
Update to be able to fetch pixbuf data and icon metadata.

* gtk/gtkicontheme.c: (theme_lookup_icon), (gtk_icon_info_free),
(icon_info_ensure_scale_and_pixbuf):
Use new cache functions.

* gtk/updateiconcache.c: (foreach_remove_func), (load_icon_data),
(maybe_cache_image_data), (scan_directory), (write_pixdata),
(get_image_meta_data_size), (get_image_pixel_data_size),
(get_image_data_size), (get_single_node_size), (get_bucket_size),
(write_bucket), (main):
Update to write pixbuf data as well as information from .icon
files.

ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-8
docs/iconcache.txt
gtk/gtkiconcache.c
gtk/gtkiconcache.h
gtk/gtkicontheme.c
gtk/updateiconcache.c

index f3b973b1522701945f932a4de0fd5618dc85d742..77366d3afdaeebdb583628c18061b39e405b5403 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2005-03-15  Anders Carlsson  <andersca@imendio.com>
+
+       * docs/iconcache.txt:
+       Update spec.
+       
+       * gtk/gtkiconcache.c: (find_image_offset),
+       (_gtk_icon_cache_get_icon_flags), (_gtk_icon_cache_add_icons),
+       (_gtk_icon_cache_get_icon), (_gtk_icon_cache_get_icon_data):
+       * gtk/gtkiconcache.h:
+       Update to be able to fetch pixbuf data and icon metadata.
+       
+       * gtk/gtkicontheme.c: (theme_lookup_icon), (gtk_icon_info_free),
+       (icon_info_ensure_scale_and_pixbuf):
+       Use new cache functions.
+       
+       * gtk/updateiconcache.c: (foreach_remove_func), (load_icon_data),
+       (maybe_cache_image_data), (scan_directory), (write_pixdata),
+       (get_image_meta_data_size), (get_image_pixel_data_size),
+       (get_image_data_size), (get_single_node_size), (get_bucket_size),
+       (write_bucket), (main):
+       Update to write pixbuf data as well as information from .icon
+       files.
+       
 2005-03-15  Tor Lillqvist  <tml@novell.com>
 
        * gdk/win32/gdkmain-win32.c (_gdk_win32_key_to_string): New
index f3b973b1522701945f932a4de0fd5618dc85d742..77366d3afdaeebdb583628c18061b39e405b5403 100644 (file)
@@ -1,3 +1,26 @@
+2005-03-15  Anders Carlsson  <andersca@imendio.com>
+
+       * docs/iconcache.txt:
+       Update spec.
+       
+       * gtk/gtkiconcache.c: (find_image_offset),
+       (_gtk_icon_cache_get_icon_flags), (_gtk_icon_cache_add_icons),
+       (_gtk_icon_cache_get_icon), (_gtk_icon_cache_get_icon_data):
+       * gtk/gtkiconcache.h:
+       Update to be able to fetch pixbuf data and icon metadata.
+       
+       * gtk/gtkicontheme.c: (theme_lookup_icon), (gtk_icon_info_free),
+       (icon_info_ensure_scale_and_pixbuf):
+       Use new cache functions.
+       
+       * gtk/updateiconcache.c: (foreach_remove_func), (load_icon_data),
+       (maybe_cache_image_data), (scan_directory), (write_pixdata),
+       (get_image_meta_data_size), (get_image_pixel_data_size),
+       (get_image_data_size), (get_single_node_size), (get_bucket_size),
+       (write_bucket), (main):
+       Update to write pixbuf data as well as information from .icon
+       files.
+       
 2005-03-15  Tor Lillqvist  <tml@novell.com>
 
        * gdk/win32/gdkmain-win32.c (_gdk_win32_key_to_string): New
index f3b973b1522701945f932a4de0fd5618dc85d742..77366d3afdaeebdb583628c18061b39e405b5403 100644 (file)
@@ -1,3 +1,26 @@
+2005-03-15  Anders Carlsson  <andersca@imendio.com>
+
+       * docs/iconcache.txt:
+       Update spec.
+       
+       * gtk/gtkiconcache.c: (find_image_offset),
+       (_gtk_icon_cache_get_icon_flags), (_gtk_icon_cache_add_icons),
+       (_gtk_icon_cache_get_icon), (_gtk_icon_cache_get_icon_data):
+       * gtk/gtkiconcache.h:
+       Update to be able to fetch pixbuf data and icon metadata.
+       
+       * gtk/gtkicontheme.c: (theme_lookup_icon), (gtk_icon_info_free),
+       (icon_info_ensure_scale_and_pixbuf):
+       Use new cache functions.
+       
+       * gtk/updateiconcache.c: (foreach_remove_func), (load_icon_data),
+       (maybe_cache_image_data), (scan_directory), (write_pixdata),
+       (get_image_meta_data_size), (get_image_pixel_data_size),
+       (get_image_data_size), (get_single_node_size), (get_bucket_size),
+       (write_bucket), (main):
+       Update to write pixbuf data as well as information from .icon
+       files.
+       
 2005-03-15  Tor Lillqvist  <tml@novell.com>
 
        * gdk/win32/gdkmain-win32.c (_gdk_win32_key_to_string): New
index 2cae3e0a43f17fa3629ccff9d6aae433a09b750b..b2d8f8f80f46da8475ff2fbbc67c105565f06f9f 100644 (file)
@@ -41,32 +41,32 @@ Regards, Matthias Clasen
 The cache file format:
 
 Header:
-2              CARD16          MAJOR_VERSION   1       
-2              CARD16          MINOR_VERSION   0       
-4              CARD32          HASH_OFFSET             
-4              CARD32          DIRECTORY_LIST_OFFSET
+2                      CARD16          MAJOR_VERSION   1       
+2                      CARD16          MINOR_VERSION   0       
+4                      CARD32          HASH_OFFSET             
+4                      CARD32          DIRECTORY_LIST_OFFSET
 
 DirectoryList:
-4              CARD32          N_DIRECTORIES           
-4*N_DIRECTORIES        CARD32          DIRECTORY_OFFSET        
+4                      CARD32          N_DIRECTORIES           
+4*N_DIRECTORIES                CARD32          DIRECTORY_OFFSET        
 
 Hash:
-4              CARD32          N_BUCKETS               
-4*N_BUCKETS    CARD32          ICON_OFFSET     
+4                      CARD32          N_BUCKETS               
+4*N_BUCKETS            CARD32          ICON_OFFSET     
 
 Icon:
-4              CARD32          CHAIN_OFFSET            
-4              CARD32          NAME_OFFSET             
-4              CARD32          IMAGE_LIST_OFFSET       
+4                      CARD32          CHAIN_OFFSET            
+4                      CARD32          NAME_OFFSET             
+4                      CARD32          IMAGE_LIST_OFFSET       
 
 ImageList:
-4              CARD32          N_IMAGES                
-8*N_IMAGES     Image           IMAGES                  
+4                      CARD32          N_IMAGES                
+8*N_IMAGES             Image           IMAGES                  
 
 Image:
-2              CARD16          DIRECTORY_INDEX 
-2              ICON_FLAGS      FLAGS                   
-4              CARD32          IMAGE_DATA_OFFSET       
+2                      CARD16          DIRECTORY_INDEX 
+2                      ICON_FLAGS      FLAGS                   
+4                      CARD32          IMAGE_DATA_OFFSET       
 
 ICON_FLAGS
 HAS_SUFFIX_PNG 1
@@ -74,6 +74,44 @@ HAS_SUFFIX_XPM       2
 HAS_SUFFIX_SVG 4
 HAS_ICON_FILE  8
 
+ImageData:
+4                      CARD32          IMAGE_PIXEL_DATA_OFFSET
+4                      CARD32          IMAGE_META_DATA_OFFSET
+
+4                      CARD32          IMAGE_PIXEL_DATA_TYPE
+4                      CARD32          IMAGE_PIXEL_DATA_LENGTH
+N/A                    N/A             PIXEL_DATA
+
+IMAGE_PIXEL_DATA_TYPE
+0 GdkPixdata format
+
+MetaData:
+4                      CARD32          EMBEDDED_RECT_OFFSET
+4                      CARD32          ATTACH_POINT_LIST_OFFSET
+4                      CARD32          DISPLAY_NAME_LIST_OFFSET
+
+EmbeddedRect:
+2                      CARD16          X0
+2                      CARD16          Y0
+2                      CARD16          X1
+2                      CARD16          Y1
+
+AttachPointList:
+4                      CARD32          N_ATTACH_POINTS
+4*N_ATTACH_POINTS      AttachPoint
+
+AttachPoint:
+2                      CARD16          X
+2                      CARD16          Y
+
+DisplayNameList:
+4                      CARD32          N_DISPLAY_NAMES
+4*N_DISPLAY_NAMES      DisplayName
+
+DisplayName:
+4                      CARD32          DISPLAY_LANG_OFFSET
+4                      CARD32          DISPLAY_NAME_OFFSET
+               
 
 Notes:
 
index fc44d036de527d5805305338e66a666799c228ea..5849549ee30ccf0a998e5a8991a378f53b93da96 100644 (file)
@@ -21,6 +21,7 @@
 #include "gtkdebug.h"
 #include "gtkiconcache.h"
 #include <glib/gstdio.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
 
 #ifdef HAVE_MMAP
 #include <sys/mman.h>
@@ -233,9 +234,9 @@ icon_name_hash (gconstpointer key)
 }
 
 gint
-_gtk_icon_cache_get_icon_flags (GtkIconCache *cache,
-                               const gchar  *icon_name,
-                               const gchar  *directory)
+find_image_offset (GtkIconCache *cache,
+                  const gchar  *icon_name,
+                  const gchar  *directory)
 {
   guint32 hash_offset;
   guint32 n_buckets;
@@ -265,8 +266,9 @@ _gtk_icon_cache_get_icon_flags (GtkIconCache *cache,
       chain_offset = GET_UINT32 (cache->buffer, chain_offset);
     }
 
-  if (!found)
+  if (!found) {
     return 0;
+  }
 
   /* We've found an icon list, now check if we have the right icon in it */
   directory_index = get_directory_index (cache, directory);
@@ -276,13 +278,28 @@ _gtk_icon_cache_get_icon_flags (GtkIconCache *cache,
   for (i = 0; i < n_images; i++)
     {
       if (GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * i) ==
-         directory_index)
-       return GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * i + 2);
+         directory_index) 
+       return image_list_offset + 4 + 8 * i;
     }
-  
+
   return 0;
 }
 
+gint
+_gtk_icon_cache_get_icon_flags (GtkIconCache *cache,
+                               const gchar  *icon_name,
+                               const gchar  *directory)
+{
+  guint32 image_offset;
+
+  image_offset = find_image_offset (cache, icon_name, directory);
+
+  if (!image_offset)
+    return 0;
+
+  return GET_UINT16 (cache->buffer, image_offset + 2);
+}
+
 void
 _gtk_icon_cache_add_icons (GtkIconCache *cache,
                           const gchar  *directory,
@@ -301,7 +318,7 @@ _gtk_icon_cache_add_icons (GtkIconCache *cache,
   
   hash_offset = GET_UINT32 (cache->buffer, 4);
   n_buckets = GET_UINT32 (cache->buffer, hash_offset);
-  
+
   for (i = 0; i < n_buckets; i++)
     {
       chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * i);
@@ -354,4 +371,140 @@ _gtk_icon_cache_has_icon (GtkIconCache *cache,
   return FALSE;
 }
                          
+GdkPixbuf *
+_gtk_icon_cache_get_icon (GtkIconCache *cache,
+                         const gchar  *icon_name,
+                         const gchar  *directory)
+{
+  guint32 offset, image_data_offset, pixel_data_offset;
+  guint32 length, type;
+  GdkPixbuf *pixbuf;
+  GdkPixdata pixdata;
+  GError *error = NULL;
+
+  offset = find_image_offset (cache, icon_name, directory);
+  
+  image_data_offset = GET_UINT32 (cache->buffer, offset + 4);
+  
+  if (!image_data_offset)
+    return NULL;
+
+  pixel_data_offset = GET_UINT32 (cache->buffer, image_data_offset);
+
+  type = GET_UINT32 (cache->buffer, pixel_data_offset);
+
+  if (type != 0)
+    {
+      GTK_NOTE (ICONTHEME,
+               g_print ("invalid pixel data type %d\n", type));
+      return NULL;
+    }
+
+  length = GET_UINT32 (cache->buffer, pixel_data_offset + 4);
+  
+  if (!gdk_pixdata_deserialize (&pixdata, length, 
+                               cache->buffer + pixel_data_offset + 8,
+                               &error))
+    {
+      GTK_NOTE (ICONTHEME,
+               g_print ("could not deserialize data: %s\n", error->message));
+      g_error_free (error);
+
+      return NULL;
+    }
+
+  pixbuf = gdk_pixbuf_from_pixdata (&pixdata, FALSE, &error);
+
+  if (!pixbuf)
+    {
+      GTK_NOTE (ICONTHEME,
+               g_print ("could not convert pixdata to pixbuf: %s\n", error->message));
+      g_error_free (error);
+
+      return NULL;
+    }
+
+  return pixbuf;
+}
+
+GtkIconData  *
+_gtk_icon_cache_get_icon_data  (GtkIconCache *cache,
+                               const gchar  *icon_name,
+                               const gchar  *directory)
+{
+  guint32 offset, image_data_offset, meta_data_offset;
+  GtkIconData *data;
+  int i;
+
+  offset = find_image_offset (cache, icon_name, directory);
+  if (!offset)
+    return NULL;
+
+  image_data_offset = GET_UINT32 (cache->buffer, offset + 4);
+  if (!image_data_offset)
+    return NULL;
+
+  meta_data_offset = GET_UINT32 (cache->buffer, image_data_offset + 4);
+  
+  if (!meta_data_offset)
+    return NULL;
+
+  data = g_new0 (GtkIconData, 1);
+
+  offset = GET_UINT32 (cache->buffer, meta_data_offset);
+  if (offset)
+    {
+      data->has_embedded_rect = TRUE;
+      data->x0 = GET_UINT16 (cache->buffer, offset);
+      data->y0 = GET_UINT16 (cache->buffer, offset + 2);
+      data->x1 = GET_UINT16 (cache->buffer, offset + 4);
+      data->y1 = GET_UINT16 (cache->buffer, offset + 6);
+    }
+
+  offset = GET_UINT32 (cache->buffer, meta_data_offset + 4);
+  if (offset)
+    {
+      data->n_attach_points = GET_UINT32 (cache->buffer, offset);
+      data->attach_points = g_new (GdkPoint, data->n_attach_points);
+      for (i = 0; i < data->n_attach_points; i++)
+       {
+         data->attach_points[i].x = GET_UINT16 (cache->buffer, offset + 4 + 4 * i); 
+         data->attach_points[i].y = GET_UINT16 (cache->buffer, offset + 4 + 4 * i + 2); 
+       }
+    }
+
+  offset = GET_UINT32 (cache->buffer, meta_data_offset + 8);
+  if (offset)
+    {
+      gint n_names;
+      gchar *lang, *name;
+      gchar **langs;
+      GHashTable *table = g_hash_table_new (g_str_hash, g_str_equal);
+
+      n_names = GET_UINT32 (cache->buffer, offset);
+      
+      for (i = 0; i < n_names; i++)
+       {
+         lang = cache->buffer + GET_UINT32 (cache->buffer, offset + 4 + 8 * i);
+         name = cache->buffer + GET_UINT32 (cache->buffer, offset + 4 + 8 * i + 4);
+         
+         g_hash_table_insert (table, lang, name);
+       }
+      
+      langs = (gchar **)g_get_language_names ();
+      for (i = 0; langs[i]; i++)
+       {
+         name = g_hash_table_lookup (table, langs[i]);
+         if (name)
+           {
+             data->display_name = g_strdup (name);
+             break;
+           }
+       }
+
+      g_hash_table_destroy (table);
+    }
+
+  return data;
+}
 
index 010ebfe939fda739b9d7bf42457b15134149e301..134a3ab317131a8ea13e527b74f5e7ce93133927 100644 (file)
 #define __GTK_ICON_CACHE_H__
 
 #include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk/gdk.h>
 
 typedef struct _GtkIconCache GtkIconCache;
+typedef struct _GtkIconData GtkIconData;
+
+struct _GtkIconData
+{
+  gboolean has_embedded_rect;
+  gint x0, y0, x1, y1;
+  
+  GdkPoint *attach_points;
+  gint n_attach_points;
+
+  gchar *display_name;
+};
 
 GtkIconCache *_gtk_icon_cache_new_for_path   (const gchar  *path);
 gboolean      _gtk_icon_cache_has_directory  (GtkIconCache *cache,
@@ -35,6 +48,12 @@ void       _gtk_icon_cache_add_icons      (GtkIconCache *cache,
 gint          _gtk_icon_cache_get_icon_flags (GtkIconCache *cache,
                                              const gchar  *icon_name,
                                              const gchar  *directory);
+GdkPixbuf    *_gtk_icon_cache_get_icon       (GtkIconCache *cache,
+                                             const gchar  *icon_name,
+                                             const gchar  *directory);
+GtkIconData  *_gtk_icon_cache_get_icon_data  (GtkIconCache *cache,
+                                             const gchar  *icon_name,
+                                             const gchar  *directory);
 
 GtkIconCache *_gtk_icon_cache_ref            (GtkIconCache *cache);
 void          _gtk_icon_cache_unref          (GtkIconCache *cache);
index 06740a7c4292418cbe513c523108d93fb090a708..b5000fd5c7d0fb1b3df96c254d3e2bc0cb6e7a5f 100644 (file)
@@ -47,8 +47,6 @@
 
 #define DEFAULT_THEME_NAME "hicolor"
 
-typedef struct _GtkIconData GtkIconData;
-
 typedef enum
 {
   ICON_THEME_DIR_FIXED,  
@@ -113,6 +111,9 @@ struct _GtkIconInfo
 #endif
   GdkPixbuf *builtin_pixbuf;
 
+  /* Cache pixbuf (if there is any) */
+  GdkPixbuf *cache_pixbuf;
+
   GtkIconData *data;
   
   /* Information about the directory where
@@ -151,17 +152,6 @@ typedef struct
   GList *dirs;
 } IconTheme;
 
-struct _GtkIconData
-{
-  gboolean has_embedded_rect;
-  gint x0, y0, x1, y1;
-  
-  GdkPoint *attach_points;
-  gint n_attach_points;
-
-  gchar *display_name;
-};
-
 typedef struct
 {
   IconThemeDirType type;
@@ -1901,7 +1891,9 @@ theme_lookup_icon (IconTheme          *theme,
       
       if (min_dir->icon_data != NULL)
        icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
-      
+      else
+       icon_info->data = _gtk_icon_cache_get_icon_data (min_dir->cache, icon_name, min_dir->subdir);
+
       if (icon_info->data == NULL &&
          min_dir->cache && has_icon_file)
        {
@@ -1923,6 +1915,12 @@ theme_lookup_icon (IconTheme          *theme,
          g_free (icon_file_path);
        }
 
+      if (min_dir->cache)
+       {
+         icon_info->cache_pixbuf = _gtk_icon_cache_get_icon (min_dir->cache, icon_name,
+                                                             min_dir->subdir);
+       }
+
       icon_info->dir_type = min_dir->type;
       icon_info->dir_size = min_dir->size;
       icon_info->threshold = min_dir->threshold;
@@ -2334,6 +2332,8 @@ gtk_icon_info_free (GtkIconInfo *icon_info)
     g_object_unref (icon_info->builtin_pixbuf);
   if (icon_info->pixbuf)
     g_object_unref (icon_info->pixbuf);
+  if (icon_info->cache_pixbuf)
+    g_object_unref (icon_info->cache_pixbuf);
   
   g_free (icon_info);
 }
@@ -2519,8 +2519,11 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info,
    */
   if (icon_info->builtin_pixbuf)
     source_pixbuf = g_object_ref (icon_info->builtin_pixbuf);
+  else if (icon_info->cache_pixbuf)
+    source_pixbuf = g_object_ref (icon_info->cache_pixbuf);
   else
     {
+
       source_pixbuf = gdk_pixbuf_new_from_file (icon_info->filename,
                                                &icon_info->load_error);
       if (!source_pixbuf)
index 076c10219d94d0a15bf94ed2d78abbe9781d7e71..8063f467a078f036f2fb4c225021967df80825ee 100644 (file)
@@ -30,6 +30,7 @@
 
 #include <glib.h>
 #include <glib/gstdio.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
 
 static gboolean force_update = FALSE;
 static gboolean quiet = FALSE;
@@ -41,6 +42,8 @@ static gboolean quiet = FALSE;
 #define HAS_SUFFIX_PNG (1 << 2)
 #define HAS_ICON_FILE  (1 << 3)
 
+#define CAN_CACHE_IMAGE_DATA(flags) (((flags) & HAS_SUFFIX_PNG) || ((flags) & HAS_SUFFIX_XPM))
+
 #define MAJOR_VERSION 1
 #define MINOR_VERSION 0
 #define HASH_OFFSET 12
@@ -82,6 +85,18 @@ typedef struct
 {
   int flags;
   int dir_index;
+
+  gboolean has_pixdata;
+  GdkPixdata pixdata;
+
+  int has_embedded_rect;
+  int x0, y0, x1, y1;
+  
+  int n_attach_points;
+  int *attach_points;
+  
+  int n_display_names;
+  char **display_names;
 } Image;
 
 static gboolean
@@ -96,6 +111,8 @@ foreach_remove_func (gpointer key, gpointer value, gpointer user_data)
     {
       g_free (key);
       g_free (image);
+      g_free (image->attach_points);
+      g_strfreev (image->display_names);
 
       return TRUE;
     }
@@ -113,6 +130,132 @@ foreach_remove_func (gpointer key, gpointer value, gpointer user_data)
   return TRUE;
 }
 
+static void
+load_icon_data (Image *image, const char *path)
+{
+  GKeyFile *icon_file;
+  char **split;
+  gsize length;
+  char *str;
+  char *split_point;
+  int i;
+  gint *ivalues;
+  GError *error = NULL;
+  gchar **keys;
+  gsize n_keys;
+  
+  icon_file = g_key_file_new ();
+  g_key_file_set_list_separator (icon_file, ',');
+  g_key_file_load_from_file (icon_file, path, G_KEY_FILE_KEEP_TRANSLATIONS, &error);
+  if (error)
+    {
+      g_error_free (error);
+      return;
+    }
+
+  ivalues = g_key_file_get_integer_list (icon_file, 
+                                        "Icon Data", "EmbeddedTextRectangle",
+                                        &length, NULL);
+  if (ivalues)
+    {
+      if (length == 4)
+       {
+         image->has_embedded_rect = TRUE;
+         image->x0 = ivalues[0];
+         image->y0 = ivalues[1];
+         image->x1 = ivalues[2];
+         image->y1 = ivalues[3];
+       }
+      
+      g_free (ivalues);
+    }
+      
+  str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
+  if (str)
+    {
+      split = g_strsplit (str, "|", -1);
+      
+      image->n_attach_points = g_strv_length (split);
+      image->attach_points = g_new (int, 2 * image->n_attach_points);
+
+      i = 0;
+      while (split[i] != NULL && i < image->n_attach_points)
+       {
+         split_point = strchr (split[i], ',');
+         if (split_point)
+           {
+             *split_point = 0;
+             split_point++;
+             image->attach_points[2 * i] = atoi (split[i]);
+             image->attach_points[2 * i + 1] = atoi (split_point);
+           }
+         i++;
+       }
+      
+      g_strfreev (split);
+      g_free (str);
+    }
+      
+  keys = g_key_file_get_keys (icon_file, "Icon Data", &n_keys, &error);
+  image->display_names = g_new0 (gchar *, 2 * n_keys + 1); 
+  image->n_display_names = 0;
+  
+  for (i = 0; i < n_keys; i++)
+    {
+      gchar *lang, *name;
+      
+      if (g_str_has_prefix (keys[i], "DisplayName"))
+       {
+         gchar *open, *close = NULL;
+         
+         open = strchr (keys[i], '[');
+
+         if (open)
+           close = strchr (open, ']');
+
+         if (open && close)
+           {
+             lang = g_strndup (open + 1, close - open - 1);
+             name = g_key_file_get_locale_string (icon_file, 
+                                                  "Icon Data", "DisplayName",
+                                                  lang, NULL);
+           }
+         else
+           {
+             lang = g_strdup ("C");
+             name = g_key_file_get_string (icon_file, 
+                                           "Icon Data", "DisplayName",
+                                           NULL);
+           }
+         
+         image->display_names[2 * image->n_display_names] = lang;
+         image->display_names[2 * image->n_display_names + 1] = name;
+         image->n_display_names++;
+       }
+    }
+
+  g_strfreev (keys);
+  
+  g_key_file_free (icon_file);
+}
+
+static void
+maybe_cache_image_data (Image *image, const gchar *path)
+{
+  if (CAN_CACHE_IMAGE_DATA(image->flags) && !image->has_pixdata) 
+    {
+      GdkPixbuf *pixbuf;
+      
+      pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+      
+      if (pixbuf) 
+       {
+         image->has_pixdata = TRUE;
+         gdk_pixdata_from_pixbuf (&image->pixdata, pixbuf, FALSE);
+       }
+    }
+}
+
 GList *
 scan_directory (const gchar *base_path, 
                const gchar *subdir, 
@@ -163,8 +306,7 @@ scan_directory (const gchar *base_path,
        }
 
       retval = g_file_test (path, G_FILE_TEST_IS_REGULAR);
-      g_free (path);
-      
+
       if (retval)
        {
          if (g_str_has_suffix (name, ".png"))
@@ -185,7 +327,10 @@ scan_directory (const gchar *base_path,
          
          image = g_hash_table_lookup (dir_hash, basename);
          if (image)
-           image->flags |= flags;
+           {
+             image->flags |= flags;
+             maybe_cache_image_data (image, path);
+           }
          else
            {
              if (!dir_added) 
@@ -203,12 +348,19 @@ scan_directory (const gchar *base_path,
              image = g_new0 (Image, 1);
              image->flags = flags;
              image->dir_index = dir_index;
-             
+             maybe_cache_image_data (image, path);
+
              g_hash_table_insert (dir_hash, g_strdup (basename), image);
            }
 
+         if (g_str_has_suffix (name, ".icon"))
+           load_icon_data (image, path);
+
          g_free (basename);
        }
+
+      g_free (path);
+
     }
 
   g_dir_close (dir);
@@ -313,6 +465,34 @@ write_card32 (FILE *cache, guint32 n)
   return i == 1;
 }
 
+
+gboolean
+write_pixdata (FILE *cache, GdkPixdata *pixdata)
+{
+  guint8 *s;
+  int len;
+  int i;
+
+
+  /* Type 0 is GdkPixdata */
+  if (!write_card32 (cache, 0))
+    return FALSE;
+
+  s = gdk_pixdata_serialize (pixdata, &len);
+
+  if (!write_card32 (cache, len))
+    {
+      g_free (s);
+      return FALSE;
+    }
+
+  i = fwrite (s, len, 1, cache);
+  
+  g_free (s);
+
+  return i == 1;
+}
+
 static gboolean
 write_header (FILE *cache, guint32 dir_list_offset)
 {
@@ -322,11 +502,64 @@ write_header (FILE *cache, guint32 dir_list_offset)
          write_card32 (cache, dir_list_offset));
 }
 
+guint
+get_image_meta_data_size (Image *image)
+{
+  gint i;
+  guint len = 0;
+
+  if (image->has_embedded_rect ||
+      image->attach_points > 0 ||
+      image->n_display_names > 0)
+    len += 12;
+
+  if (image->has_embedded_rect)
+    len += 8;
+
+  if (image->n_attach_points > 0)
+    len += 4 + image->n_attach_points * 4;
+
+  if (image->n_display_names > 0)
+    {
+      len += 4 + 8 * image->n_display_names;
+
+      for (i = 0; image->display_names[i]; i++)
+       len += ALIGN_VALUE (strlen (image->display_names[i]) + 1, 4);
+    }
+
+  return len;
+}
+
+guint
+get_image_pixel_data_size (Image *image)
+{
+  if (image->has_pixdata)
+    return image->pixdata.length + 8;
+
+  return 0;
+}
 
 guint
-get_single_node_size (HashNode *node)
+get_image_data_size (Image *image)
+{
+  guint len;
+  
+  len = 0;
+
+  len += get_image_pixel_data_size (image);
+  len += get_image_meta_data_size (image);
+  
+  if (len > 0)
+    len += 8;
+
+  return len;
+}
+
+guint
+get_single_node_size (HashNode *node, gboolean include_image_data)
 {
   int len = 0;
+  GList *list;
 
   /* Node pointers */
   len += 12;
@@ -336,7 +569,16 @@ get_single_node_size (HashNode *node)
 
   /* Image list */
   len += 4 + g_list_length (node->image_list) * 8;
-
+  /* Image data */
+  if (include_image_data)
+    for (list = node->image_list; list; list = list->next)
+      {
+       Image *image = list->data;
+       
+       len += get_image_data_size (image);
+      }
+  
   return len;
 }
 
@@ -347,7 +589,7 @@ get_bucket_size (HashNode *node)
 
   while (node)
     {
-      len += get_single_node_size (node);
+      len += get_single_node_size (node, TRUE);
 
       node = node->next;
     }
@@ -360,8 +602,11 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
 {
   while (node != NULL)
     {
-      int next_offset = *offset + get_single_node_size (node);
-      int i, len;
+      int next_offset = *offset + get_single_node_size (node, TRUE);
+      int image_data_offset = *offset + get_single_node_size (node, FALSE);
+      int data_offset;
+      int tmp;
+      int i, j, len;
       GList *list;
          
       /* Chain offset */
@@ -381,7 +626,8 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
        return FALSE;
       
       /* Image list offset */
-      if (!write_card32 (cache, *offset + 12 + ALIGN_VALUE (strlen (node->name) + 1, 4)))
+      tmp = *offset + 12 + ALIGN_VALUE (strlen (node->name) + 1, 4);
+      if (!write_card32 (cache, tmp))
        return FALSE;
       
       /* Icon name */
@@ -393,10 +639,15 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
       if (!write_card32 (cache, len))
        return FALSE;
       
+      /* Image data goes right after the image list */
+      tmp += 4 + len * 8;
+
       list = node->image_list;
+      data_offset = image_data_offset;
       for (i = 0; i < len; i++)
        {
          Image *image = list->data;
+         int image_data_size = get_image_data_size (image);
          
          /* Directory index */
          if (!write_card16 (cache, image->dir_index))
@@ -405,14 +656,151 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
          /* Flags */
          if (!write_card16 (cache, image->flags))
            return FALSE;
-         
+
          /* Image data offset */
-         if (!write_card32 (cache, 0))
-           return FALSE;
-         
+         if (image_data_size > 0)
+           {
+             if (!write_card32 (cache, data_offset))
+               return FALSE;
+             data_offset += image_data_size;
+           }
+         else
+           {
+             if (!write_card32 (cache, 0))
+               return FALSE;
+           }
+
          list = list->next;
        }
 
+      /* Now write the image data */
+      list = node->image_list;
+      for (i = 0; i < len; i++, list = list->next)
+       {
+         Image *image = list->data;
+         int pixel_data_size = get_image_pixel_data_size (image);
+         int meta_data_size = get_image_meta_data_size (image);
+
+         if (meta_data_size + pixel_data_size == 0)
+           continue;
+
+         /* Pixel data */
+         if (pixel_data_size > 0) 
+           {
+             if (!write_card32 (cache, image_data_offset + 8))
+               return FALSE;
+           }
+         else
+           {
+             if (!write_card32 (cache, 0))
+               return FALSE;
+           }
+
+         if (meta_data_size > 0)
+           {
+             if (!write_card32 (cache, image_data_offset + pixel_data_size + 8))
+               return FALSE;
+           }
+         else
+           {
+             if (!write_card32 (cache, 0))
+               return FALSE;
+           }
+
+         if (pixel_data_size > 0)
+           {
+             if (!write_pixdata (cache, &image->pixdata))
+               return FALSE;
+           }
+         
+         if (meta_data_size > 0)
+           {
+             int ofs = image_data_offset + pixel_data_size + 20;
+
+             if (image->has_embedded_rect)
+               {
+                 if (!write_card32 (cache, ofs))
+                   return FALSE;
+             
+                 ofs += 8;
+               }             
+             else
+               {
+                 if (!write_card32 (cache, 0))
+                   return FALSE;
+               }
+             
+             if (image->n_attach_points > 0)
+               {
+                 if (!write_card32 (cache, ofs))
+                   return FALSE;
+
+                 ofs += 4 + 4 * image->n_attach_points;
+               }
+             else
+               {
+                 if (!write_card32 (cache, 0))
+                   return FALSE;
+               }
+
+             if (image->n_display_names > 0)
+               {
+                 if (!write_card32 (cache, ofs))
+                   return FALSE;
+               }
+             else
+               {
+                 if (!write_card32 (cache, 0))
+                   return FALSE;
+               }
+
+             if (image->has_embedded_rect)
+               {
+                 if (!write_card16 (cache, image->x0) ||
+                     !write_card16 (cache, image->y0) ||
+                     !write_card16 (cache, image->x1) ||
+                     !write_card16 (cache, image->y1))
+                   return FALSE;
+               }
+
+             if (image->n_attach_points > 0)
+               {
+                 if (!write_card32 (cache, image->n_attach_points))
+                   return FALSE;
+                 
+                 for (j = 0; j < 2 * image->n_attach_points; j++)
+                   {
+                     if (!write_card16 (cache, image->attach_points[j]))
+                       return FALSE;
+                   }             
+               }
+
+             if (image->n_display_names > 0)
+               {
+                 if (!write_card32 (cache, image->n_display_names))
+                   return FALSE;
+
+                 ofs += 4 + 8 * image->n_display_names;
+
+                 for (j = 0; j < 2 * image->n_display_names; j++)
+                   {
+                     if (!write_card32 (cache, ofs))
+                       return FALSE;
+
+                     ofs += ALIGN_VALUE (strlen (image->display_names[j]) + 1, 4);
+                   }
+
+                 for (j = 0; j < 2 * image->n_display_names; j++)
+                   {
+                     if (!write_string (cache, image->display_names[j]))
+                       return FALSE;
+                   }        
+               }
+           }
+
+         image_data_offset += pixel_data_size + meta_data_size + 8;
+       }
+      
       *offset = next_offset;
       node = node->next;
     }
@@ -643,6 +1031,7 @@ main (int argc, char **argv)
   if (!force_update && is_cache_up_to_date (path))
     return 0;
 
+  g_type_init ();
   build_cache (path);
   
   return 0;